<script lang="ts">
// @ts-nocheck
import {defineComponent, PropType} from 'vue'
import {LyricsInfo} from "@/components/MusicPlay/LyricsInfo";
import htmlElementUtils from "@/utils/htmlElementUtils";
import {LyricUpdateEl} from "@/components/MusicPlay/LyricUpdateEl";
import VirtualScroller from "@/components/VirtualScroller/VirtualScroller.vue";

/**
 * 歌词显示组件，定义歌词显示数据，然后按照这个数据显示就行了
 */
export default defineComponent({
  name: "LyricList",
  components:{
    VirtualScroller
  },
  props: {
    time: {// 播放到了什么时间，单位是毫秒
      type:Number,
      default:()=>0
    },
    fontSize: {// 播放到了什么时间，单位是毫秒
      type:String,
      default:()=>'20px'
    },
    lineHeight: {// 播放到了什么时间，单位是毫秒
      type:Number,
      default:()=>1.5
    },
    lyricsInfos: {
      type: Array as PropType<LyricsInfo[]>,
      default: ()=> []
    },
    activeColor: {
      type:String,
      default: 'red'
    },
    color: {
      type: String,
      default: '#1831cb'
    }
  },
  directives: {
    LyricUpdateEl,
  },
  emits: ['nowLrc','playStart'],
  watch: {
    time: {
      immediate: true,
      handler() {
        let indexResult = 0
        indexResult = this.lyricsInfos.findLastIndex(itemData=>{
          // 记录显示到了第几行
          return this.time>=itemData.startTime
        })
        if(indexResult !== -1 && this.showIndex !== indexResult) {
          this.showIndex = indexResult
          this.$emit('nowLrc',this.lyricsInfos[this.showIndex].lrc.join(''))
        }
      }
    },
    showIndex: 'runList',
    fontSize: 'runList',
    lyricsInfos: 'runList',
    activeColor: 'runList',
    color: 'runList',
    lineHeight: 'runList',
  },
  data() {
    return {
      height: 0,
      width: 0,
      showIndex: 0,
      dragging: false,
      clientY: 0,
      scrollTop: 0,
      clearTimeNumer: null,
      clickTime: false,
    }
  },
  methods: {
    onResize({height,width}) {
      this.height = height
      this.width = width
      this.runList()
    },
    runList() {
      if (this.clickTime) return
      if(this.$refs.VirtListRef?.$el) {
        /**
         * bottom: 683
         * current: 20
         * top : 663 // 距离元素顶部的位置
         */
        this.$refs.VirtListRef.scrollIntoView(this.showIndex)
        // 刷新一下
        this.lyricsInfos.forEach((item, index) => {
          this.getRowWordStyle(item, index)
        })
      }
    },
    getRowWord(index) {
      return this.$refs['rowWord'+index] as HTMLDivElement
    },
    /**
     * 控制歌词行内滚动
     * @param itemData
     * @param index
     */
    getRowWordStyle(itemData: LyricsInfo, index: number) {
      let rowWord = this.getRowWord(index)
      if(!rowWord || !this.width || !this.height) {
        return
      }
      let scale3d = this.showIndex === index ? `scale3d(${this.lineHeight},${this.lineHeight},1)` : '';
      let width = parseFloat(htmlElementUtils.getStyle(rowWord,'width'))
      let colorPer = 0
      let notShowWidth = (width * (this.showIndex === index ? this.lineHeight : 1) - this.width) / 2;
      if(notShowWidth) {
        rowWord.style.transform = ''
      }
      if(this.showIndex === index) {
        rowWord.style.transform = scale3d
      }
      if (index > this.showIndex) {
        colorPer = 0
        if(notShowWidth>0){
          rowWord.style.transform = `translate3d(${notShowWidth}px,0px,0px) ${scale3d}`
        }
      } else if (this.showIndex > index) {
        colorPer = 100
        if(notShowWidth>0){
          rowWord.style.transform = `translate3d(${-notShowWidth}px,0px,0px) ${scale3d}`
        }
      } else {
        let find = itemData.kTimeWithSE.find(i => {
          return i[0]>=this.time && this.time<=i[1]
        })
        if(find) {
          colorPer = (this.time - itemData.startTime) / (itemData.entTime - itemData.startTime) * 100
          if(notShowWidth>0){
            let perWidth = width * colorPer / 100
            if(perWidth* this.lineHeight < this.width / 2) {
              rowWord.style.transform = `translate3d(${notShowWidth}px,0px,0px) ${scale3d}`
            } else {
              let wordSize = parseFloat(this.fontSize) * this.lineHeight * 2
              let nowNotShowWidth = notShowWidth - (perWidth + wordSize - this.width / 2) * this.lineHeight
              if(nowNotShowWidth < -notShowWidth) {
                nowNotShowWidth = -notShowWidth
              }
              // 处理往回滚动的问题
              if(nowNotShowWidth > notShowWidth) {
                nowNotShowWidth = notShowWidth
              }
              rowWord.style.transform = `translate3d(${nowNotShowWidth}px,0px,0px) ${scale3d}`
            }
          }
        } else {
          // 如果还在这一句歌曲但是又没有找到时间范围，说明也是结束了
          colorPer = 100
          if(notShowWidth>0){
            rowWord.style.transform = `translate3d(${-notShowWidth}px,0px,0px) ${scale3d}`
          }
        }
      }
      if (colorPer > 100) {
        colorPer = 100
      }
      if (colorPer < 0) {
        colorPer = 0
      }
      rowWord.style['background-position-x'] = `${100 - colorPer}%`
    },
    initPos(clientY){
      this.dragging = true
      // 拖动以后允许一段时间点击时间
      if(this.clearTimeNumer) {
        clearTimeout(this.clearTimeNumer)
      }
      this.clickTime = true
      this.clearTimeNumer = setTimeout(()=>{
        this.clickTime = false
        this.runList()
      },6000) // 6秒钟
      this.clientY = clientY
      let parentEl = this.$refs.VirtListRef.getScollerEl()
      this.scrollTop = parentEl.scrollTop
    },
    removePos(clientY) {
      if(!this.dragging) return
      let altY = clientY - this.clientY
      let parentEl = this.$refs.VirtListRef.getScollerEl()
      parentEl.scrollTop = this.scrollTop - altY
    },
    handleMouseDown(event:MouseEvent) {
      this.initPos(event.clientY)
    },
    handleTouchStart(event:TouchEvent) {
      this.initPos(event.touches[0].clientY)
    },
    handleMouseMove(event: DragEvent) {
      this.removePos(event.clientY)
    },
    handleTouchMove(event: TouchEvent) {
      this.removePos(event.touches[0].clientY)
    },
    handleMouseUp() {
      this.dragging = false
    },
    playStartClick(itemData: LyricsInfo, index: number) {
      this.$emit('playStart',itemData.startTime)
    },
  }
})
</script>

<template>
  <el-auto-resizer class="LyricList" :onResize="onResize">
    <VirtualScroller
      :list="lyricsInfos.map(i=>{
        return {
          ...i,
          id: i.lrc.join('') + i.startTime
        }
      })"
      itemKey="id"
      ref="VirtListRef"
      :minSize="parseFloat(fontSize) * lineHeight"
      fixed
      :buffer="5"
      @mousedown.prevent="handleMouseDown"
      @touchstart="handleTouchStart"
      @mousemove.prevent="handleMouseMove"
      @touchmove.prevent="handleTouchMove"
      @mouseup.prevent="handleMouseUp"
      @touchend="handleMouseUp"
    >
      <template #default="{itemData, index}">
        <div class="lycRow"
             :ref="'lycRow'+index">
          <div class="rowWord"
               :class="{activeRowWord: showIndex === index}"
               v-lyric-update-el="{
                  immediate: true,
                  time,
                  func:()=>{
                      getRowWordStyle(itemData,index)
                  }
                }"
               :ref="'rowWord'+index">
            {{itemData.lrc.join('')}}
          </div>
          <el-button class="playStart"
                     v-if="clickTime"
                     @click="playStartClick(itemData,index)"
                     icon="VideoPlay"
                     text
          >
          </el-button>
        </div>
      </template>
    </VirtualScroller>
  </el-auto-resizer>
</template>

<style scoped lang="scss">
.LyricList{
  ::v-deep{
    .vue-recycle-scroller{
      overflow: hidden !important;
      cursor: move;
    }
  }
  .lycRow{
    display: flex;
    justify-content: center;
    align-items: center;
    overflow: hidden;
    position: relative;
    pointer-events: none;
    .playStart{
      position: absolute;
      right: 0;
      cursor: pointer;
      pointer-events: all;
      padding: 0;
      font-size: 1em;
    }
    .rowWord{
      font-size: v-bind(fontSize);
      white-space: pre;
      transition: all 0.2s linear;
      line-height: v-bind(lineHeight) !important; // 这里设置为1 元素高度就和fontSize大小一致了
      padding: 0 !important;
      margin: 0 !important;
      background-clip: text !important;
      color: transparent !important; // 设置成透明就可以完全用背景色控制颜色
      background-image: linear-gradient(to right,v-bind(activeColor) 50%, v-bind(color) 0px) !important;
      background-repeat: no-repeat !important;
      background-size: 200% 100% !important;
      background-position-x: 100%;
    }
    .activeRowWord{
      will-change: background-position-x;
    }
  }
}
</style>
